package gkaylee

import (
	"fmt"
	"io"
	"log/slog"
	"net/http"
	"net/url"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"
)

// Deprecated: Stack 堆栈信息
type Stack struct {
	Filename string
	FuncName string
	Line     int
}

// Deprecated: Stacks 堆栈信息
type Stacks []*Stack

func (Stacks) getShortPath(path string) string {
	var char rune
	l := strings.Count(path, "/")
	if l == 0 {
		l = strings.Count(path, "\\")
		if l > 3 {
			char = '\\'
		}
	} else if l > 3 {
		char = '/'
	}
	if char > 0 {
		location := len(path)
		for range 3 {
			location = strings.LastIndex(path[:location], string(char))
		}
		return path[location:]
	}
	return path
}

func (s Stacks) Error() string {
	result := new(strings.Builder)
	if len(s) > 0 {
		for x := len(s) - 1; x >= 0; x-- {
			filename := s.getShortPath(s[x].Filename)
			funcName := filepath.Base(s[x].FuncName)
			_, _ = fmt.Fprintf(result, "file: %s, func: %s, line: %d; \n", filename, funcName, s[x].Line)
		}
		return result.String()[:result.Len()-1]
	}
	return ""
}

// Deprecated: PanicError panic错误
type PanicError struct {
	Stacks   Stacks
	PanicVal any
}

func (p *PanicError) Error() string {
	result := new(strings.Builder)
	stacksString := p.Stacks.Error()
	if len(stacksString) > 0 {
		_, _ = fmt.Fprintln(result, stacksString)
	}
	_, _ = fmt.Fprintf(result, "----%#v", p.PanicVal)
	return result.String()
}

// Deprecated: PanicIfError error不为nil, 引发panic, 否则返回val. PanicIfError1 别名
func PanicIfError[T any](val T, err error) T {
	return PanicIfError1(val, err)
}

// Deprecated: PanicIfError0 error有一个不为nil, 则引发panic
func PanicIfError0(errs ...error) {
	if errs != nil {
		for _, err := range errs {
			if err != nil {
				panic(err)
			}
		}
	}
}

// Deprecated: PanicIfError1 error不为nil, 引发panic, 否则返回val.
func PanicIfError1[T any](val T, err error) T {
	if err != nil {
		PanicIfError0(err)
	}
	return val
}

// Deprecated: IgnoreErr err不为nil, 使用slog.Warn打印, 并附带堆栈信息. 无论任何情况, 返回val. IgnoreErr1 别名
func IgnoreErr[T any](val T, err error) T {
	return IgnoreErr1(val, err)
}

// Deprecated: IgnoreErr0 使用slog.Warn打印不为nil的error, 并附带堆栈信息
func IgnoreErr0(errs ...error) {
	for _, err := range errs {
		if err != nil {
			// 获取堆栈信息
			stacks := GetStacks(1, 0)
			slog.Warn("ignoreErr", "stacks", stacks, "error", err)
		}
	}
}

// Deprecated: IgnoreErr1 err不为nil, 使用slog.Warn打印, 并附带堆栈信息. 无论任何情况, 返回val.
func IgnoreErr1[T any](val T, err error) T {
	IgnoreErr0(err)
	return val
}

// Deprecated: Try 拦截f函数执行过程出现的panic, 并包装成error返回, 如果f没有出现panic, 将f返回的内容做为函数的返回值. Try1 别名.
func Try[T any](f func() T) (T, error) {
	return Try1(f)
}

// Deprecated: Try0 拦截f执行过程出现的panic, 并包装成error返回
func Try0(f func()) (errValue error) {
	lastPC, _, _, _ := runtime.Caller(1)
	defer RecoverError(1, lastPC, &errValue)
	f()
	return nil
}

// Deprecated: Try1 拦截f函数执行过程出现的panic, 并包装成error返回, 如果f没有出现panic, 将f返回的内容做为函数的返回值.
func Try1[T any](f func() T) (T, error) {
	var interReturn T
	err := Try0(func() {
		interReturn = f()
	})
	return interReturn, err
}

// Deprecated: RecoverError 将panic恢复到错误, 并赋值到err变量. skip和cutoff的作用同 GetStacks.
func RecoverError(skip uint, cutoff uintptr, err *error) {
	*err = RecoverReturnError(1+skip, cutoff)
}

// Deprecated: RecoverReturnError 将panic恢复到错误, 并返回. skip和cutoff的作用同 GetStacks.
func RecoverReturnError(skip uint, cutoff uintptr) error {
	if v := recover(); v != nil {
		pe := new(PanicError)
		pe.PanicVal = v
		pe.Stacks = GetStacks(1+skip, cutoff)
		return pe
	}
	return nil
}

// Deprecated: GetStacks 获取堆栈信息. skip表示向上跳过多少层(0为当前调用位置开始), cutoff表示截至到何处(0为直到程序入口出).
func GetStacks(skip uint, cutoff uintptr) Stacks {
	var stacks Stacks
	// 跳过当前行和 runtime/panic.go文件
	s := 2 + int(skip)
	for {
		pc, file, line, ok := runtime.Caller(s)
		s++
		if !ok || strings.HasSuffix(file, "/runtime/proc.go") ||
			strings.HasSuffix(file, "/testing/testing.go") {
			// 遇到测试文件或程序入口表示到头了, 跳出循环
			break
		}
		if cutoff != 0 && pc == cutoff {
			l := len(stacks)
			if l > 1 {
				stacks = stacks[:l-1]
			}
			break
		}
		matched, _ := regexp.MatchString("/gkaylee.*?/error.go$", file)
		if matched {
			// 如果panic是本文件触发的, 不展示本文件的代码堆栈
			continue
		}
		funcName := runtime.FuncForPC(pc).Name()
		stacks = append(stacks, &Stack{
			Filename: file,
			Line:     line,
			FuncName: funcName,
		})
	}
	return stacks
}

// Deprecated: URLError 封装url.Error, 并附带响应内容
type URLError struct {
	BaseInfo    url.Error
	RespPayload []byte
}

func (u *URLError) Error() string {
	return fmt.Sprintf("%s %q(%q): %s", u.BaseInfo.Op, u.BaseInfo.URL, u.RespPayload, u.BaseInfo.Err)
}

func (u *URLError) Unwrap() error { return &u.BaseInfo }

// Deprecated: NewURLError 封装url.Error, 并附带响应内容
func NewURLError(resp *http.Response, err error) *URLError {
	data, _ := io.ReadAll(resp.Body)
	return &URLError{
		BaseInfo: url.Error{
			Op:  resp.Request.Method,
			URL: resp.Request.URL.String(),
			Err: err,
		},
		RespPayload: data,
	}
}

// Deprecated: StatusCodeError 状态码错误
type StatusCodeError struct {
	ExpectCode int
	ActualCode int
}

func (s *StatusCodeError) Error() string {
	return fmt.Sprintf("status code must be %d, but got %d", s.ExpectCode, s.ActualCode)
}

// Deprecated: NewStatusCodeError 状态码错误
func NewStatusCodeError(expectCode int, resp *http.Response) *URLError {
	return NewURLError(resp, &StatusCodeError{
		ExpectCode: expectCode,
		ActualCode: resp.StatusCode,
	})
}

// Deprecated: NotSupperContentTypeError 不支持的Content-Type
type NotSupperContentTypeError struct {
	ContentType string
}

func (n *NotSupperContentTypeError) Error() string {
	return fmt.Sprintf("Unprocessable Content-Type: %s", n.ContentType)
}

// Deprecated: NewNotSupperContentTypeError 不支持的Content-Type
func NewNotSupperContentTypeError(resp *http.Response) *URLError {
	return NewURLError(resp, &NotSupperContentTypeError{
		ContentType: resp.Header.Get("Content-Type"),
	})
}
